package com.spyu.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Map;
import java.util.Objects;

/**
 * 反射工具类
 *
 * @author spyu
 * @date 2023-03-08
 */
public final class ReflectUtils {

    /**
     * 反射方式设置变量值
     * @param t         对象
     * @param fieldName 变量名
     * @param value     修改后的变量值
     * @param <T>       对象所属的类
     */
    public static <T> void setFieldValue(T t, String fieldName, Object value) {
        try {
            Class<?> clazz = t.getClass();
            Field field = clazz.getDeclaredField(fieldName);

            if (Modifier.isPrivate(field.getModifiers())) {
                setAccessible(field);
            }

            field.set(t, value);
        } catch (NoSuchFieldException | IllegalAccessException e1) {
            throw new RuntimeException(e1);
        }
    }

    /**
     * 反射方式执行方法
     * @param t             对象
     * @param methodName    方法名
     * @param paramTypeArr  方法的参数类型数组
     * @param paramValueArr 方法的参数值数组
     * @param <T>           对象所属的类
     */
    public static <T> void invokeMethod(T t, String methodName,
                                        Class<? extends Object>[] paramTypeArr,
                                        Object[] paramValueArr) {
        try {
            Class<?> clazz = t.getClass();
            Method method = clazz.getMethod(methodName, paramTypeArr);

            if (Modifier.isPrivate(method.getModifiers())) {
                setAccessible(method);
            }

            method.invoke(t, paramValueArr);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 实例化对象
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T newInstance(String clazz) {
        try {
            return (T) Class.forName(clazz).newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取对象的属性值
     * @param t
     * @param fieldName
     * @param <T>
     * @return
     */
    public static <T> Object getFieldValue(T t, String fieldName) {
        try {
            Class<?> clazz = t.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            setAccessible(field);
            return field.get(t);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置方法可访问
     * @param t
     * @param <T>
     * @return
     */
    public static <T extends AccessibleObject> T setAccessible(T t) {
        if (!Objects.isNull(t) && !t.isAccessible()) {
            t.setAccessible(true);
        }

        return t;
    }

    /**
     * 获取对象上的所有注解
     * @param t
     * @param <T>
     * @return
     */
    public static <T> Annotation[] getAnnotations(T t) {
        Class<?> clazz = t.getClass();
        return clazz.getAnnotations();
    }

    /**
     * 获取属性field上的注解K
     * @param field
     * @param annotationClazz
     * @param <K>
     * @return
     */
    public static <K extends Annotation> K getAnnotationOnField(Field field, Class<K> annotationClazz) {
        return field.getAnnotation(annotationClazz);
    }

    /**
     * 获取方法method上的注解K
     * @param method
     * @param annotationClazz
     * @param <K>
     * @return
     */
    public static <K extends Annotation> K getAnnotationOnMethod(Method method, Class<K> annotationClazz) {
        return method.getAnnotation(annotationClazz);
    }


    /**
     * 修改操作对象里面变量山的指定注解
     * @param t               操作的对象
     * @param annotationClazz 修改的注解
     * @param replaceValues   修改的值
     * @param <T>
     * @param <V>
     */
    public static <T, V extends Annotation> void modifyAnnotation(T t, Class<V> annotationClazz,
                                                                  Map<String, Object> replaceValues) {
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();

        try {
            for (Field field : fields) {
                setAccessible(field);
                V v = field.getAnnotation(annotationClazz);

                if (!Objects.isNull(v)) {
                    InvocationHandler handler = Proxy.getInvocationHandler(v);
                    Field annotationField = handler.getClass().getDeclaredField("memberValues");
                    setAccessible(annotationField);
                    Map<String, Object> memberValues = (Map<String, Object>) annotationField.get(handler);
                    memberValues.putAll(replaceValues);
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
